/**
 * 宏宇软件 前端UI插件
 *
 * @modify 2014-03-06
 * @author sylthasgg@gmail.com
 * @version 1.0
 */
(function($, window, document) {
	// namespace
	window["hy"] = window["hy"] || {};
	// jQuery bind
	hy.$ = $;
	hy.jQuery = $;
	// dom bind
	hy.$window = window;
	hy.$document = document;

	/**
	 * HTML标签组装函数
	 *
	 * @param s 含有html标签的字符串
	 * 参数说明
	 * E                           htmlz("div") = "<div></div>"
	 * E#id                        htmlz("div#main") = "<div id="main"></div>"
	 * E.class                     htmlz("div.ui.message") = "<div class="ui message"></div>"
	 * E {prop:value}              htmlz("div {width:200px,height:100px;}") = "<div style="width:200px,height:100px;"></div>"
	 * E [prop=value,..]           htmlz("input [type='text',name='user']") = "<input type="text" name="user" />"
	 * E+E                         htmlz("div+p") = "<div></div><p></p>"
	 * E>E                         htmlz("div>p") = "<div><p></p></div>"
	 * E*N                       htmlz("div>ul#list>li.item*3") =  "<div><ul id="list"><li class="item"></li><li class="item"></li><li class="item"></li></ul></div>"
	 */
	var htmlz = function(s) {
		return htmlz.method["main"].call({}, s);
	};
	hy.htmlz = htmlz;

	// 正则表达式
	htmlz.regexp = {
		"node": /([a-zA-Z]\w*)/i, // 节点
		"id": /\#([a-zA-Z_]\w+)/i, // id
		"class": /\.([a-zA-Z_][\w\.]+)/i, // 类
		"style": /\{([\s\S]*?)\}/i, // 样式
		"attr": /\[(.*)\]/i, // 属性
		"split": /(\+|>)/, // 分隔符
		"num": /\*\s*(\d+)/, // 数量
		"singletag": /br|hr|img|input|param|meta|link/ // 单标签
	};
	htmlz.method = {
		main: function(s) {
			if (!s || !/^[a-zA-Z][\s\S]/.test(s)) return;
			var reg = htmlz.regexp;
			var nodes = s.split(reg["split"]);
			this[0] = [];
			var parent = this[0];
			for (var i in nodes) {
				var node = nodes[i];
				if (node == ">") {
					parent[parent.length - 1]["child"] = [];
					parent = parent[parent.length - 1]["child"];
				}
				if (i % 2 == 0) {
					htmlz.method["builder"].call(this, node, parent);
				}
			}
			return htmlz.method["recombine"].call(this, this[0]);
		},
		builder: function(node, parent) {
			var reg = htmlz.regexp,
				_node = reg["node"].exec(node),
				_id = reg["id"].exec(node),
				_class = reg["class"].exec(node),
				_style = reg["style"].exec(node),
				_attr = reg["attr"].exec(node),
				_num = reg["num"].exec(node);
			parent.push({
				"string": node,
				"node": (_node) ? _node[1] : null,
				"id": (_id) ? _id[1] : null,
				"class": (_class) ? _class[1].split(".").join(" ") : null,
				"style": (_style) ? _style[1] : null,
				"num": (_num) ? _num[1] : 1,
				"attribute": (_attr) ? _attr[1].split(",").join(" ") : null
			});
		},
		recombine: function(nodes) {
			var reg = htmlz.regexp,
				string = "";
			for (var i in nodes) {
				var node = nodes[i];
				var isSingleton = reg["singletag"].test(node["node"]);
				string += "<" + node["node"];
				if (node["id"]) {
					string += " id=\"" + node["id"] + "\"";
				}
				if (node["class"]) {
					string += " class=\"" + node["class"] + "\"";
				}
				if (node["style"]) {
					string += " style=\"" + node["style"] + "\"";
				}
				if (node["attribute"]) {
					string += " " + node["attribute"].replace(/'/g, "\"");
				}
				string += isSingleton ? "/>" : ">";
				if (node["child"]) {
					var child = htmlz.method["recombine"](node["child"]);
					string += child;
				}
				if (!isSingleton) string += "</" + node["node"] + ">";
				if (node["num"] > 1) {
					var temp = string;
					for (var i = node["num"]; i-- > 1;) {
						string += temp;
					}
				}
			}
			return string;
		}
	};

	/**
	 * 字符串格式化
	 *
	 * 参数说明
	 * format("hello! {0}, {1}.","tom","welcome") = "hello tom, welcome."
	 */
	hy.format = function() {
		var str = arguments[0],
			args = [];
		for (var i = 1,
				il = arguments.length; i < il; i++) {
			args.push(arguments[i]);
		}
		return str.replace(/\{(\d+)\}/g,
			function(m, i) {
				return args[i];
			});
	};

	/**
	 * 判断对象是否为数组
	 *
	 * 参数说明
	 * @param o javascrip对象
	 * isArray({}) = false;
	 * isArray([]) = true;
	 */
	hy.isArray = function(o) {
		return Object.prototype.toString.call(o) === "[object Array]";
	};

	/**
	 * 消息插件函数
	 *
	 * @param msg 消息内容
	 * 参数说明
	 * 参照帮助手册
	 */
	var message = function(msg) {
		var type = typeof arguments[1],
			arg = arguments[1],
			options = {};
		if (type == "string") {
			options["type"] = arg;
		} else if (type == "boolean") {
			options["modal"] = arg;
		} else if (arg && hy.isArray(arg)) {
			options["position"] = arg;
		} else if (type == "object") {
			options = arg;
		}
		return message.method["initialize"].call({}, msg, options);
	};

	hy.message = message;

	message.defaults = {
		type: "info", // 类型: info - 消息, success - 成功, warning - 警告, error - 错误, confirm - 确认
		modal: false, // 模态
		shadow_bgcolor: "#666", // 阴影背景色
		shadow_opacity: 0.9, // 阴影透明度
		show_close: true, // 显示关闭按钮
		auto_close: false, // 自动关闭
		animation: "fade up", // 显示动画效果
		onClose: function() {}, // 关闭事件
		onConfirm: function(b) {} // 确认对话框回调事件 
	};
	message.method = {
		initialize: function(msg, options) {
			if (!msg) return;
			options = $.extend({}, message.defaults, options || {});
			this.$$options = options;
			message.method["dialog"].call(this, msg, options);
		},
		dialog: function(msg, options) {
			var confirm = options["type"] == "confirm";
			var type = confirm ? "warning" : options["type"];
			// 对话框主体
			this.$dialog = $(htmlz("div.ui.compact.{0}.message{position:fixed;z-index:1001;display:none;}".replace(/\{\d+\}/i, type)));
			// 关闭按钮
			if (options["show_close"]) {
				this.$close = $(htmlz("i.close.icon")).appendTo(this.$dialog);
				this.$dialog.css("padding-right", "25px");
			}
			// 标题
			if (options["title"]) {
				this.$title = $(htmlz("div.header")).append(options["title"]).appendTo(this.$dialog);
			}
			//消息内容
			this.$message = $(htmlz("div.content")).html(msg).appendTo(this.$dialog);
			// 确认对话 type="confirm"
			if (confirm) {
				this.$actions = $(htmlz("div.actions {text-align:center;margin-top:10px;}")).appendTo(this.$dialog);
				this.$confirm = $(htmlz("a.ui.green.label")).append("确认").appendTo(this.$actions);
				this.$cancel = $(htmlz("a.ui.red.label")).append("取消").appendTo(this.$actions);
				this.$dialog.css("min-width", "200px");
			}
			// 是否模态
			(options["modal"]) && message.method["shadow"].call(this, options);

			this.$dialog.appendTo(document.body);
			// 事件绑定
			message.method["event"].call(this);
			// 位置
			message.method["position"].call(this, options["position"]);
			// 显示
			this.$dialog.transition(options["animation"]);
			// 自动关闭
			if (options["auto_close"]) {
				var self = this;
				setTimeout(function() {
					message.method["destroy"].call(self);
				}, 3000);
			}
		},
		shadow: function(options) {
			var str = "{background:" + options["shadow_bgcolor"] + ";z-index:1000;width:100%;height:100%;position: fixed; left: 0px; top: 0px; opacity: " + options["shadow_opacity"] + ";filter:Alpha(Opacity=" + options["shadow_opacity"] * 100 + ");}";
			this.$shadow = $(htmlz("div" + str)).appendTo(document.body);
		},
		event: function() {
			var self = this,
				options = self.$$options;
			// 关闭按钮事件
			if (self.$close) {
				self.$close.bind("click", function(e) {
					message.method["destroy"].call(self);
					return;
				});
			}
			// 确认对话框按钮事件
			if (this.$actions) {
				this.$confirm.bind("click", function(e) {
					options["onConfirm"].call(self, true);
					message.method["destroy"].call(self);
					return true;
				});
				this.$cancel.bind("click", function(e) {
					options["onConfirm"].call(self, false);
					message.method["destroy"].call(self);
					return false;
				});
			}
			// 非模态时页面鼠标按下事件
			if (!options["modal"] && !options["auto_close"]) {
				$(document).bind("mousedown.message", function(e) {
					var p = $(e.target).closest("div.ui.compact.message");
					(!p.length) && message.method["destroy"].call(self);
				});
			}
		},
		position: function(position) {
			var css = {};
			if (!position) {
				var w = this.$dialog.outerWidth(true) / 2;
				var h = this.$dialog.outerHeight(true) / 2;
				css = {
					top: "50%",
					left: "50%",
					margin: (-h) + "px 0px 0px " + (-w) + "px"
				};
			} else {
				for (var i in position) {
					var prop = position[i].split(":");
					if (/(top|right|bottom|left)/i.test(prop[0]) && /\d+/i.test(prop[1])) {
						css[prop[0]] = prop[1] + "px";
					}
					css["margin"] = "0px";
				}
			}
			this.$dialog.css(css);
		},
		destroy: function() {
			(this.$$options) && this.$$options["onClose"].call(self);
			// DOM 移除
			(this.$dialog) && this.$dialog.remove();
			(this.$shadow) && this.$shadow.remove();
			// event move
			$(document).unbind(".message");
			for (var i in this) {
				delete this[i];
			}
		}
	};

	/**
	 * 分页模型
	 *
	 * @property totalRows 总记录
	 * @property pageSize 每页显示记录
	 * @property startRow 起始记录
	 * @property currentPage 当前页
	 * @property totalPages 总页数
	 *
	 * @method initialize 初始化
	 * @method turning 翻页
	 * @method hasPrev 是否有上一页
	 * @method hasNext 是否有下一页
	 * @method serialize 序列化
	 * @method json 转为JSON字符串
	 * @method reset 重置每页显示记录
	 */

	function Pager(totalRows, pageSize) {
		this.totalRows = totalRows || 0;
		this.pageSize = pageSize || 30;
		this.startRow = 0;
		this.currentPage = 1;
		this.totalPages = 0;
		this.initialize();
	};

	Pager.prototype = {
		initialize: function() {
			this.totalPages = Math.floor(this.totalRows / this.pageSize);
			if (this.totalRows % this.pageSize != 0) {
				this.totalPages++;
			};
		},
		turning: function(pageNo) {
			this.currentPage = pageNo || 1;
			this.startRow = (this.currentPage - 1) * this.pageSize;
		},
		hasPrev: function() {
			return this.currentPage > 1;
		},
		hasNext: function() {
			return this.currentPage < this.totalPages;
		},
		serialize: function() {
			var sr = [];
			for (var k in this) {
				(typeof this[k] != "function") && sr.push(k + "=" + this[k]);
			}
			return sr.join("&");
		},
		json: function() {
			var sr = [];
			for (var k in this) {
				(typeof this[k] != "function") && sr.push("\"" + k + "\":" + this[k]);
			}
			return "{" + sr.join(",") + "}";
		},
		reset: function(pageSize) {
			this.pageSize = pageSize || 30;
			this.initialize();
		}
	};

	/**
	 * 获取分页模型
	 *
	 * @param totalRows 总记录
	 * @param pageSize 每页显示记录
	 */
	var pager = function(totalRows, pageSize) {
		return new Pager(totalRows, pageSize);
	};
	hy.pager = pager;

	//分页条插件
	var pagination = function(container) {
		var arg = arguments[1],
			options = {};
		if (typeof arg == "number") {
			options["totalRows"] = arg;
		} else if (typeof arg == "object") {
			options = arg;
		}
		return pagination.method["initialize"].call({}, container, options);
	};
	hy.pagination = pagination;
	// 默认配置
	pagination.defaults = {
		totalRows: 0, // 总页数
		pageSize: 10, // 每页显示记录
		style: "default", // defualt | mini
		pageList: null, // 用户自定义每页显示记录 [10,20,30...]
		jump: false, // mini 样式时跳转功能
		onChange: function(pager) {} // 翻页操作触发回调函数(分页模型)
	};

	pagination.styles = {
		"default": {
			"$list_wrap": "div.ui.item",
			"$list": "div.ui.small.dropdown",
			"$first": "a.item>i.step.backward.icon",
			"$prev": "a.item>i.horizontally.flipped.play.basic.icon",
			"$input_wrap": "div.ui.item>div.ui.action.input",
			"$input": "input [type='text',size='1']",
			"$btn": "div.ui.mini.button",
			"$next": "a.item>i.play.basic.icon",
			"$last": "a.item>i.step.forward.icon",
			"$info": "div.item",
			"infomation": "共{pages}页{rows}条记录 当前显示{from}到{to}条记录"
		},
		"mini": {
			"$first": "a.item>i.step.backward.icon",
			"$prev": "a.item>i.horizontally.flipped.play.basic.icon",
			"$info": "a.item [data-content='双击输入页码回车跳转']",
			"$input_wrap": "div.item  {display:none;}>div.ui.input",
			"$input": "input [type='text',size='1']",
			"$next": "a.item>i.play.basic.icon",
			"$last": "a.item>i.step.forward.icon",
			"infomation": "{current}/{pages}"
		}
	};
	pagination.method = {
		initialize: function(container, options) {
			if (!container) return;
			// 清空容器
			options = $.extend({}, pagination.defaults, options || {});
			this.$container = $(container).html("");
			// 分页模型
			this.$$pager = hy.pager(options["totalRows"], options["pageSize"]);
			var style = options["style"];
			var html = pagination.styles[style];
			options["info"] = options["info"] || html["infomation"];
			var build = function(s) {
				return $(htmlz(html[s])).appendTo(this.$container);
			};
			/** 创建分页按钮**/
			// 默认样式
			if (style == "default" && options["pageList"]) {
				this.$list_wrap = build.call(this, "$list_wrap");
				this.$list = $(htmlz(html["$list"])).appendTo(this.$list_wrap);
				pagination.method["pagelist"].call(this, options["pageList"]);
			}
			this.$first = build.call(this, "$first");
			this.$prev = build.call(this, "$prev");
			if (style == "mini") {
				this.$info = build.call(this, "$info");
			}
			this.$input_wrap = build.call(this, "$input_wrap");
			this.$input = $(htmlz(html["$input"])).appendTo(this.$input_wrap.find("div"));
			if (style == "default") {
				this.$btn = $(htmlz(html["$btn"])).append("go").appendTo(this.$input_wrap.find("div"));
			}
			this.$next = build.call(this, "$next");
			this.$last = build.call(this, "$last");
			(style == "default") && (this.$info = build.call(this, "$info"));

			// 事件绑定
			pagination.method["event"].call(this, options);
			// 重置分页条
			pagination.method["reset"].call(this, options);
			// destroy
			this.onDestroy = pagination.method["destroy"];
			return this;
		},
		pagelist: function(list) {
			var self = this,
				drop = this.$list;
			drop.append($(htmlz("div.text")).append("每页记录").append(htmlz("i.dropdown.icon")));
			var menu = $(htmlz("div.menu")).appendTo(drop);
			for (var i in list) {
				menu.append($(htmlz("div.item [data-value='" + list[i] + "']")).append("<p>" + list[i] + "</p>"));
			}
			// 激活下拉列表-设置默认的每页显示记录
			drop.dropdown('set').selected(this.$$pager.pageSize);
			// 设置每页显示记录
			drop.dropdown({
				onChange: function(value, text) {
					var pager = self.$$pager;
					pager.reset(value);
					self.go(pager.currentPage);
				}
			});
		},
		event: function(options) {
			var self = this;
			var dis = function(o) {
				return $(o).hasClass("disabled");
			};
			// 翻页函数
			var go = function() {
				var pager = this.$$pager;
				var num = arguments[0] || 1;
				if (num < 1) {
					num = 1;
				} else if (num > pager.totalPages) {
					num = pager.totalPages > 0 ? pager.totalPages : 1;
				}
				// 对象翻页
				pager.turning(num);
				// 重置分页条
				pagination.method["reset"].call(self, options);
				// 回调函数
				options["onChange"].call(this, pager);
			};
			this.go = go;
			this.$first && (this.$first.click(function() {
				!dis(this) && go.call(self, 1);
			}));
			this.$prev && (this.$prev.click(function() {
				!dis(this) && go.call(self, self.$$pager.currentPage - 1);
			}));
			this.$next && (this.$next.click(function() {
				!dis(this) && go.call(self, self.$$pager.currentPage + 1);
			}));
			this.$last && (this.$last.click(function() {
				!dis(this) && go.call(self, self.$$pager.totalPages);
			}));

			// default 样式下按钮的事件
			this.$btn && (this.$btn.click(function() {
				var num = self.$input.val();
				if (/^[0-9]{1,4}$/i.test(num)) {
					go.call(self, num - 0);
				}
			}));
			// 文本输入框只能输入数字
			this.$input && (this.$input.on("keyup", function(e) {
				this.value = this.value.replace(/[^\d]/g, '');
			}));
			// mini style popup tips
			if (options["style"] == "mini" && options["jump"]) {
				this.$info.popup();
				this.$info.bind("dblclick.pagination.info", function(e) {
					$(this).hide();
					self.$input_wrap.show();
				});
				// 回车提交
				this.$input.on("keyup.event", function(e) {
					var key = e.keyCode || e.which;
					if (key == 13) {
						go.call(self, $(this).val() - 0);
						self.$input_wrap.hide();
						self.$info.show();
					}
				});
			}
		},
		reset: function(options) {
			pagination.method["states"].call(this);
			this.$input && (this.$input.val(this.$$pager.currentPage));
			pagination.method["infomation"].call(this, options);
		},
		infomation: function(options) {
			if (!this.$$pager.totalRows) return;
			var pager = this.$$pager;
			var end = pager.startRow + pager.pageSize;
			end = end > pager.totalRows ? pager.totalRows : end;

			// 参数
			var params = {
				rows: pager.totalRows,
				pages: pager.totalPages,
				current: pager.currentPage,
				from: pager.startRow + 1,
				to: end
			}
			// 替换占位
			this.$info.html(options["info"].replace(/\{(\w+)\}/g, function(m, i) {
				return params[i];
			}));
		},
		states: function() {
			var dis = "disabled";
			var pager = this.$$pager;
			if (pager.hasPrev()) {
				this.$first.removeClass(dis);
				this.$prev.removeClass(dis);
			} else {
				this.$first.addClass(dis);
				this.$prev.addClass(dis);
			}
			if (pager.hasNext()) {
				this.$next.removeClass(dis);
				this.$last.removeClass(dis);
			} else {
				this.$next.addClass(dis);
				this.$last.addClass(dis);
			}
		},
		destroy: function() {
			this.$container && this.$container.html("");
			for (var i in this) {
				delete this[i];
			}
		}
	};

	// 下拉选择框 dropdown扩展
	var selection = function(container) {
		var arg = arguments[1],
			options = {};
		if (typeof arg == "object") {
			options = arg;
		}
		return selection.method["initialize"].call({}, container, options);
	};

	hy.selection = selection;

	selection.styles = {
		"$text": "div.text",
		"$menu": "div.menu",
		"$item": "div.item"
	};

	selection.method = {
		initialize: function(container, options) {
			if (!container) return;
			var styles = selection.styles;
			// HTML Elements
			this.$container = $(container);
			this.$input = options["input"] ? $(options["input"]) : null;
			this.$text = $(htmlz(styles["$text"])).html("请选择");
			this.$menu = $(htmlz(styles["$menu"]));
			this.$container.append(this.$text).append(htmlz("i.dropdown.icon")).append(this.$menu);
			// Variables
			this.textField = options["textField"] || "text";
			this.valueField = options["valueField"] || "id";
			this.defaultValue = options["defaultValue"] || null;
			this.url = options["url"] || null;
			this.data = options["data"] || null;
			this.method = options["method"] || "post";
			this.params = options["params"] || {};
			// Methods
			this.onChange = options["onChange"] || function(value, text) {};
			this.onDestroy = selection.method["destroy"];
			selection.method["load"].call(this);
			// Return
			return this;
		},
		load: function() {
			var self = this;
			if (this.url && !this.data) {
				$.ajax({
					url: this.url,
					type: this.method,
					dataType: "json",
					data: this.params,
					success: function(data) {
						self.data = data;
						(data && data.length) && selection.method["draw"].call(self);
					},
					error: function(xhr, status, error) {
						self.$text.html("error" + error);
					}
				});
			} else {
				selection.method["draw"].call(this);
			}
		},
		draw: function() {
			var self = this,
				data = self.data;
			// Draw Items
			for (var i = 0, len = data.length; i < len; i++) {
				var d = data[i],
					text = d[this.textField],
					value = d[this.valueField];
				(typeof value != "number") && (value = "'" + value + "'");
				var item = $(htmlz(selection.styles["$item"] + "[data-text='" + text + "',data-value=" + value + "]"));
				item.append(text).appendTo(this.$menu);
			}
			// Selected Default Value
			this.defaultValue && this.$container.dropdown('set').selected(this.defaultValue);
			// Callback Method
			this.$container.dropdown({
				onChange: function(value, text) {
					self.$input && self.$input.val(value);
					self.onChange.call(self, value, text);
				}
			});
		},
		destroy: function() {
			for (var i in this) {
				delete this[i];
			}
		}
	};
	// Tree
	var tree = function(container) {
		var arg = arguments[1],
			options = {};
		if (typeof arg == "object") {
			options = arg;
		}
		return tree.method["initialize"].call(this, container, options);
	}

	hy.tree = tree;
	// Default Settings
	tree.defaults = {
		method: "post",
		multiple: false,
		params: {},
		url: null,
		data: null,
		convert: true,
		textField: "text",
		valueField: "id",
		parentField: "pid",
		rootValue: 0,
		filter: function(data) {
			return data["checked"];
		},
		onError: function() {},
		onSelected: function(o) {},
		onChecked: function(values, texts) {}
	};

	tree.css = {
		collapse: "icon collapse",
		expand: "icon expand",
		file: "icon file",
		closed: "icon folder",
		open: "icon folder open",
		empty: "icon empty checkbox",
		check: "icon checked checkbox",
	};

	tree.styles = {
		"$node": "div.node {cursor:pointer;}",
		"$indent": "span.indent"
	};
	tree.method = {
		initialize: function(container, options) {
			if (!container) return;
			// HTML Elements
			this.$container = $(container);
			// Copy Attributes
			options = $.extend({}, tree.defaults, options || {});
			$.extend(this, options);
			this.css = $.extend({}, tree.css, options["css"] || {});
			// Variables
			this.checked = [];
			tree.method["load"].call(this);
			// Methods
			this.getSelected = tree.method["getSelected"];
			this.getAllSelected = tree.method["getAllSelected"];
			this.openNode = tree.method["openNode"];
			// Checked
			this.checked.reverse();
			for (var i in this.checked) {
				tree.method["operation"].call(this, this.checked[i]);
			}
			return this;
		},
		load: function() {
			var self = this;
			if (!this.data && this.url) {
				$.ajax({
					url: this.url,
					type: this.method,
					dataType: "json",
					data: this.params,
					success: function(data) {
						self.data = data || [];
						tree.method["draw"].call(self);
					},
					error: function() {
						self.onError.apply(self, arguments);
					}
				});
			} else {
				tree.method["draw"].call(self);
			}
		},
		draw: function() {
			var self = this,
				_data = [];
			// convert data
			if (this.convert) {
				var data = this.data;
				var func = function(data, pid, arr) {
					for (var i in data) {
						var d = data[i];
						if (pid == d[self.parentField]) {
							arr.push(d);
							if (hasChild(d[self.valueField])) {
								d["children"] = [];
								arguments.callee(data, d[self.valueField], d["children"]);
							}
						}
					}
				};
				var hasChild = function(id) {
					for (var i in data) {
						if (data[i][self.parentField] == id) {
							return true;
						}
					}
					return false;
				}
				func(data, self.rootValue, _data);
			} else {
				_data = this.data;
			}

			for (var i in _data) {
				tree.method["buildTree"].call(this, _data[i], this.$container, 0);
			}
			this.data = _data;
		},
		buildTree: function(data, parent, index) {
			// indent = 2 * index - 1;
			var self = this,
				children = data["children"],
				state = data["state"];
			var hasChild = children && children.length,
				isOpen = state != "closed",
				indent = (index > 0) ? 2 * index - 1 : 0;
			var styles = tree.styles;
			var li = $(htmlz("li"));
			var node = $(htmlz(styles["$node"])).appendTo(li);
			// insert indent
			while (indent-- > 0) {
				node.append(htmlz(styles["$indent"]));
			}
			// icons
			var css = {};
			for (var i in this.css) {
				css[i] = this.css[i].replace(/\s/g, ".");
			}
			if (hasChild && isOpen) {
				node.append(htmlz("i." + css["collapse"])).append(htmlz("i." + css["open"]));
			} else if (hasChild) {

				node.append(htmlz("i." + css["expand"])).append(htmlz("i." + css["closed"]));
			} else {
				node.append(htmlz("i." + css["file"]));
			}

			// input
			if (this.multiple) {
				node.append(htmlz("i." + css["empty"]));
				this.filter(data) && (this.checked.push(node.find("i:last")[0]));
			}
			node.append($(htmlz("span.title")).append(data[this.textField]));
			node.attr(this.valueField, data[this.valueField]);
			// mouse over
			node.bind("mouseover", function() {
				$(this).addClass("hover");
			});
			node.bind("mouseout", function() {
				$(this).removeClass("hover");
			});
			// node click
			node.bind("click", function(e) {
				var elem = e.srcElement || e.target,
					nodeName = elem.nodeName.toLocaleLowerCase();
				var clazz = elem.className;
				// change the node's class
				if ((nodeName == "span" && clazz.indexOf("title") != -1) || (nodeName == "i" && (clazz.indexOf("file") != -1 || clazz.indexOf("folder") != -1))) {
					if (self.lastSelected) self.lastSelected.removeClass("selected");
					var selected = $(this).addClass("selected");
					self.lastSelected = selected;
					self.onSelected.call(self, selected[0].data);
				}
				tree.method["operation"].call(self, e);
			})
			// node parent
			parent[0].node && (node[0].parent = {
				node: parent[0].node
			});
			node[0].data = data;
			if (!this.datas) this.datas = {};
			this.datas[data[this.valueField]] = {
				data: data,
				node: node[0]
			};
			if (hasChild) {
				++index;
				var ul = $("<ul></ul>").appendTo(li);
				ul[0].node = node[0];
				!isOpen && ul.css("display", "none");
				for (var i = 0; i < children.length; i++) {
					tree.method["buildTree"].call(this, children[i], ul, index);
				}
				node[0].child = [];
				// child node
				var ns = ul.find(">li>div");
				for (var i = 0; i < ns.length; i++) {
					node[0].child.push({
						node: ns[i],
						data: children[i]
					});
				}
			}
			// insert to dom
			li.appendTo(parent);
		},
		operation: function(e) {
			var elem = e.srcElement || e.target || e,
				nodeName = elem.nodeName.toLocaleLowerCase();
			var clazz = elem.className;
			var css = this.css;
			// collapse & expand 展开和收缩
			if (nodeName == "i" && (clazz.indexOf("collapse") != -1 || clazz.indexOf("expand") != -1)) {
				var isOpen = clazz.indexOf("collapse") != -1;
				var ul = $(elem.parentNode.parentNode).find("ul")[0];
				var i = $(elem.parentNode).find("i");
				isOpen ? $(i[0]).removeClass(css["collapse"]).addClass(css["expand"]) : $(i[0]).removeClass(css["expand"]).addClass(css["collapse"]);
				isOpen ? $(i[1]).removeClass(css["open"]).addClass(css["closed"]) : $(i[1]).removeClass(css["closed"]).addClass(css["open"]);
				ul.style.display = isOpen ? "none" : "block";
			};
			// checkbox
			if (nodeName == "i" && clazz.indexOf("checkbox") != -1) {
				var isCheck = (clazz.indexOf("checked") != -1);
				// css stylte
				isCheck ? $(elem).removeClass(css["check"]).addClass(css["empty"]) : $(elem).removeClass(css["empty"]).addClass(css["check"]);
				var node = elem.parentNode;
				if (!this.selected) this.selected = {};
				var id = node["data"][this.valueField];
				var exist = (this.selected[id] != undefined);
				// this.selected setting
				!isCheck ? !exist && (this.selected[id] = id) : exist && delete this.selected[id];
				tree.method["findChild"].call(this, node, !isCheck);
				tree.method["findParent"].call(this, node, !isCheck);
				var values = [],
					texts = [];

				if (this.selected && this.datas) {
					for (var i in this.selected) {
						values.push(i);
						texts.push(this.datas[i]["data"][this.textField]);
					}
				}
				this.onChecked.call(this, values, texts);
			}
		},
		findParent: function(node, b) {
			if (!node.parent) return;
			var li = node.parent["node"].parentNode,
				isAll = true,
				id = node.parent["node"]["data"][this.valueField],
				input = $(node.parent["node"]).find("i").filter(".checkbox"),
				isCheck = input.hasClass("checked");
			var css = this.css;
			if (b) {
				$.each(node.parent["node"].child, function(i, o) {
					var _input = $(o.node).find("i").filter(".checkbox"),
						_isCheck = _input.hasClass("checked");
					if (!_isCheck) {
						isAll = false;
						return false;
					}
				});
				if (isAll) {
					!isCheck && (input.removeClass(css["empty"]).addClass(css["check"]));
					!(id in this.selected) && (this.selected[id] = id);
					tree.method["findParent"].call(this, node.parent["node"], b);
				}
			} else {
				isCheck && (input.removeClass(css["check"]).addClass(css["empty"]));
				(id in this.selected) && delete this.selected[id];
				tree.method["findParent"].call(this, node.parent["node"], b);
			}
		},
		findChild: function(node, b) {
			if (!node.child) return;
			var self = this;
			var css = this.css;
			b ? $.each(node.child, function(i, o) {
				var input = $(o.node).find("i").filter(".checkbox"),
					isCheck = input.hasClass("checked"),
					id = o.data[self.valueField];
				!(id in self.selected) && (self.selected[id] = id);
				!isCheck && input.removeClass(css["empty"]).addClass(css["check"]);
				o.node.child && tree.method["findChild"].call(self, o.node, b);
			}) : $.each(node.child, function(i, o) {
				var input = $(o.node).find("i").filter(".checkbox"),
					isCheck = input.hasClass("checked"),
					id = o.data[self.valueField];
				(id in self.selected) && delete self.selected[id];
				isCheck && input.removeClass(css["check"]).addClass(css["empty"]);
				o.node.child && tree.method["findChild"].call(self, o.node, b);
			});
		},
		getSelected: function() {
			if (!this.lastSelected) return false;
			var node = this.lastSelected[0];
			return node.data[this.valueField];
		},
		getAllSelected: function() {
			if (!this.selected) return false;
			var arr = [];
			for (i in this.selected) arr.push(i);
			return arr.join(",");
		},
		openNode: function(id) {
			if (id in this.datas) {
				var css = this.css;
				var node = this.datas[id]["node"];
				var ul = node.parentNode.parentNode;
				while (ul.style.display === "none") {
					ul.style.display = "block";
					node = node.parent["node"];
					var i = $(node).find("i");
					$(i[0]).removeClass(css["expand"]).addClass(css["collapse"]);
					$(i[1]).removeClass(css["closed"]).addClass(css["open"]);
					ul = node.parentNode.parentNode;
				}
			}
			return this;
		}
	};

	// Combo Tree
	var combotree = function(container) {
		var arg = arguments[1],
			options = {};
		if (typeof arg == "object") {
			options = arg;
		}
		return combotree.method["initialize"].call({}, container, options);
	};

	hy.combotree = combotree;

	combotree.styles = {
		"$inputWrap": "div.ui.icon.input {width:100%;}",
		"$input": "input[type='text',readonly='readonly']",
		"$icon": "i.down.arrow.icon",
		"$treeWrap": "div.wrap",
		"$tree": "ul.ui.tree"
	}

	combotree.method = {
		initialize: function(container, options) {
			if (!container) return;
			options = options || {};
			// HTML Elements
			var self = this,
				styles = combotree.styles;
			this.$container = $(container);
			this.$inputWrap = $(htmlz(styles["$inputWrap"])).appendTo(this.$container);
			this.$input = $(htmlz(styles["$input"])).appendTo(this.$inputWrap);
			this.$inputWrap.append(htmlz(styles["$icon"]));

			this.$treeWrap = $(htmlz(styles["$treeWrap"])).appendTo(this.$container).hide();
			this.$tree = $(htmlz(styles["$tree"])).appendTo(this.$treeWrap);
			// Callback Methods
			var func = function(value, text) {
				this.$input.val(text);
				this.$container.attr("data-value", value);
			};
			this.onSelected = options["onSelected"] || function() {};
			this.onChecked = options["onChecked"] || function() {};
			var opts = $.extend({}, {}, options);
			// Tree Instance
			opts["onSelected"] = function(o) {
				if (!this.multiple) {
					var value = o[this.valueField],
						text = o[this.textField];
					func.call(self, value, text);
					self.onSelected.call(self, value, text);
				}
			};
			opts["onChecked"] = function(value, text) {
				func.call(self, value, text);
				self.onChecked.call(self, value, text);
			};
			this.tree = tree(this.$tree, opts);
			// Events
			this.$input.on("click", function() {
				self.$treeWrap.show();
			});
			// Document event bind
			$(document).bind("mousedown.combo", function(e) {
				var p = $(e.target).closest("div.ui.combo.tree");
				if (p.length) return;
				self.$treeWrap.hide();
			});

			this.onDestroy = combotree.method["destroy"];
			return this;
		},
		destroy: function() {
			$(document).unbind(".combo");
			this.$container && this.$container.html("");
			for (var i in this) {
				delete this[i];
			}
		}
	};

	// Combo
	var combo = function(container) {
		var arg = arguments[1],
			options = {};
		if (typeof arg == "object") {
			options = arg;
		}
		return combo.method["initialize"].call({}, container, options);
	};

	hy.combo = combo;

	combo.styles = {
		"$inputWrap": "div.ui.icon.input {width:100%;}",
		"$input": "input[type='text',readonly='readonly']",
		"$icon": "i.down.arrow.icon",
		"$wrap": "div.wrap",
		"$item": "div.item"
	};

	combo.method = {
		initialize: function(container, options) {
			if (!container) return;
			// HTML Elements
			var self = this,
				styles = combo.styles;
			this.$container = $(container);
			this.$inputWrap = $(htmlz(styles["$inputWrap"])).appendTo(this.$container);
			this.$input = $(htmlz(styles["$input"])).appendTo(this.$inputWrap);
			this.$inputWrap.append(htmlz(styles["$icon"]));
			this.$wrap = $(htmlz(styles["$wrap"])).appendTo(this.$container).hide();
			// Variables
			this.textField = options["textField"] || "text";
			this.valueField = options["valueField"] || "id";
			this.defaultValue = options["defaultValue"] || null;
			this.url = options["url"] || null;
			this.data = options["data"] || null;
			this.method = options["method"] || "post";
			this.params = options["params"] || {};
			// Methods
			this.onChange = options["onChange"] || function(value, text) {};
			this.onError = options["onError"] || function() {};
			this.onDestroy = combo.method["destroy"];

			// Events bind
			this.$input.on("click", function() {
				self.$wrap.show();
			});
			$(document).bind("mousedown.combo", function(e) {
				var p = $(e.target).closest("div.ui.combo");
				if (p.length) return;
				self.$wrap.hide();
			});

			combo.method["load"].call(this);
			// Return
			return this;
		},
		load: function() {
			var self = this;
			if (!this.data && this.url) {
				$.ajax({
					url: this.url,
					type: this.method,
					dataType: "json",
					data: this.params,
					success: function(data) {
						self.data = data || [];
						combo.method["draw"].call(self);
					},
					error: function() {
						self.onError.apply(self, arguments);
					}
				});
			} else {
				combo.method["draw"].call(self);
			}
		},
		draw: function() {
			var self = this,
				data = this.data;
			// items
			for (var i = 0, len = data.length; i < len; i++) {
				var d = data[i],
					text = d[this.textField],
					value = d[this.valueField];
				(typeof value != "number") && (value = "'" + value + "'");
				var item = $(htmlz(combo.styles["$item"] + "[data-text='" + text + "',data-value=" + value + "]"));
				item.append(text).appendTo(this.$wrap);
				// bind events
				item.bind("click", function(e) {
					var value = $(this).attr("data-value");
					var text = $(this).attr("data-text");
					self.$input.val(text);
					$(this).addClass("active");
					// the last selected
					if (self.lastSelected) {
						self.lastSelected.removeClass("active");
					}
					self.lastSelected = $(this);
					self.$wrap.hide();
					// callback method
					self.onChange.call(self, value, text);
				});
				item.bind("mouseover", function() {
					$(this).addClass("hover");
				});
				item.bind("mouseout", function() {
					$(this).removeClass("hover");
				});
			}
			// default value
			if (this.defaultValue) {
				this.$wrap.find("div").each(function(i, o) {
					if (self.defaultValue == $(o).attr("data-value")) {
						// 事件委派
						$(o).trigger("click");
					}
				});
			}
		},
		destroy: function() {
			$(document).unbind(".combo");
			this.$container && this.container.html("");
			for (var i in this) {
				delete this[i];
			}
		}
	};

	// Table sort plugin
	var tablesort = function(table) {
		var arg = arguments[1],
			options = {};
		if (typeof arg == "object") {
			options = arg;
		} else if (typeof arg == "string") {
			options["attr"] = arg;
		}
		return tablesort.method["initialize"].call({}, table, options);
	};

	hy.tablesort = tablesort;

	tablesort.parser = {
		int: function(val) {
			val = parseInt(val) || 0;
			return val;
		},
		float: function(val) {
			val = parseFloat(val) || 0;
			return val;
		},
		date: function(val) {
			if (typeof val == "string") {
				val = val.replace(/-/g, "/");
			}
			val = new Date(Date.parse(val)) || 0;
			return val;
		},
		bool: function(val) {
			return (val == true || String(val).toLowerCase() === "true") ? 1 : 0;
		},
		string: function(val) {
			val = val.toString() || "";
			return val;
		},
		defaults: function(val) {
			val = val.toString() || "";
			return val;
		}
	};

	tablesort.defaults = {
		"asc": 'sorted ascending',
		"desc": 'sorted descending',
		"attr": "order"
	}

	tablesort.regexp = {
		"int": /^-?\d+$/,
		"float": /^(-?\d+)(\.\d+)+$/,
		"date": /^\d{4}(-|\/)\d{2}(-|\/)\d{2}(\s\d{2}:\d{2}(:\d{2})?)?$/
	};
	tablesort.method = {
		initialize: function(table, options) {
			if (!table) return;
			options = $.extend({}, tablesort.defaults, options);
			var self = this;
			// HTML Elements
			this.$table = $(table);
			this.$th = null;
			// Variables
			this.asc = options["asc"];
			this.desc = options["desc"];
			this.attr = options["attr"];
			this.sorts = options["sorts"] || {};
			this.index = null;
			this.type = {};
			this.direction = {};
			// bind events
			var isMultiple = this.$table.find("thead>tr").length > 1;
			this.isMultiple = isMultiple;
			var index = 0;
			this.$table.find("thead>tr>th").each(function(i, o) {
				// 多级表头判定
				if (isMultiple && !$(o).attr(self.attr)) {
					return;
				}
				var idx = $(o).attr("index") || $(o).attr("hide-index");
				(idx == undefined) && (idx = index++);
				$(o).data("sort-index", ~~idx);
				$(o).bind("click.sort", function() {
					tablesort.method["sort"].call(self, this);
				});
			});
			this.onDestroy = tablesort.method["destroy"];
			return this;
		},
		sort: function(th) {
			var self = this,
				table = this.$table;
			// get td html
			var getValue = function(td) {
				var val = td.html();
				var chk = td.find("input[type='checkbox']");
				var rad = td.find("input[type='radio']");
				if (chk.length) {
					val = chk.attr("checked") == "checked";
				} else if (rad.length) {
					val = rad.attr("checked") == "checked";
				}
				return val;
			}
			// parse method
			var parseValue = function(val, type) {
				!(type in tablesort.parser) && (type = "string");
				return tablesort.parser[type](val);
			};
			// get type
			var getType = function(val) {
				if (typeof val == "boolean") {
					return "bool";
				}
				var regs = tablesort.regexp;
				for (var i in regs) {
					if (regs[i].test(val)) {
						return i;
					}
				}
				return "string";
			};
			// table rows
			var rows = [];
			table.find("tbody>tr").each(function(i, o) {
				rows.push(o);
			});
			// remove class
			table.find("thead>tr>th").removeClass(this.asc).removeClass(this.desc);

			// attribute
			var order = $(th).attr(this.attr);
			var sorter = this.sorts[order] || {};

			var index = $(th).data("sort-index");
			(sorter["index"]) && (index = sorter["index"]);
			if (typeof sorter == "number") {
				index = sorter;
			}
			// read type from type's cache
			var type = this.type[index] || sorter["type"] || $(th).attr("type");
			if (!type && (typeof sorter == "string")) {
				type = sorter;
			}

			// index diff
			if (this.index != index) {
				this.direction[index] = "desc";
			} else {
				this.direction[index] = this.direction[index] === "desc" ? "asc" : "desc";
			}
			var direction = this.direction[index] === "asc" ? 1 : -1;

			this.index = index;
			// customer get value method
			if (sorter["value"]) {
				getValue = sorter["value"];
			}
			// sort method
			rows.sort(function(a, b) {
				a = $(a.cells[index]);
				b = $(b.cells[index]);
				var _av = getValue(a),
					_bv = getValue(b);
				if (!type) {
					type = getType(_av);
					// push to the type cache
					self.type[index] = type;
				}
				var _a = parseValue(_av, type);
				var _b = parseValue(_bv, type);
				var result = (type == "string") ? _a.localeCompare(_b) : (_a - _b);
				return result * direction;
			});

			var oFrag = document.createDocumentFragment();
			for (var i in rows) {
				oFrag.appendChild(rows[i]);
			}
			table.find("tbody").append(oFrag);
			this.$th = $(th).addClass(this[this.direction[index]]);
		},
		destroy: function() {
			this.$table.find("thead>th").unbind(".sort");
			this.$table.find("thead>tr>th").each(function(i, o) {
				$(o).removeData("sort-index");
			});
			for (var i in this) {
				delete this[i];
			}
		}
	};

	// Table Hide Plugin
	var tablehide = function(table) {
		var arg = arguments[1],
			options = {};
		var type = typeof arg;
		if (type == "object") {
			options = arg;
		} else if (type == "string") {
			options["title"] = arg;
		} else if (type == "function") {
			options["filter"] = arg;
		}
		return tablehide.method["initialize"].call({}, table, options);
	};

	hy.tablehide = tablehide;

	tablehide.styles = {
		"$container": "div.ui.hidebar",
		"$header": "div.header.item",
		"$close": "i.close.icon {cursor:pointer;}",
		"$wrapItem": "div.wrap.item",
		"$list": "div.ui.list",
		"$item": "div.item>div.ui.checkbox",
		"$checkbox": "input[type='checkbox']"
	};

	tablehide.method = {
		initialize: function(table, options) {
			if (!table) return;
			options = options || {};
			// Variables
			var self = this;
			this.title = options["title"] || "显示/隐藏列";
			this.attr = options["attr"] || "attr";
			this.identity = options["identity"] || "hid";
			this.uper = options["uper"] || "uhid"
			this.list = [];
			// HTML Elements
			var sts = tablehide.styles;
			this.$table = $(table);
			this.$container = $(htmlz(sts["$container"]));
			var hd = $(htmlz(sts["$header"])).append(htmlz(sts["$close"]) + this.title);
			this.$container.append(hd);
			var wrap = $(htmlz(sts["$wrapItem"])).appendTo(this.$container);
			this.$list = $(htmlz(sts["$list"])).appendTo(wrap);
			var bm = $(htmlz(sts["$header"])).append(htmlz(sts["$close"]) + this.title);
			this.$container.append(bm);
			// Events
			this.$container.find("i.close.icon").bind("click", function() {
				self.$container.fadeOut("fast");
			});
			// isMultiple
			var isMultiple = this.$table.find("thead>tr").length > 1;
			this.isMultiple = isMultiple;
			if (isMultiple) {
				var ths = this.$table.find("thead>tr>th");
				ths.each(function(i, o) {
					var uhid = $(o).attr(self.uper);
					$(o).attr("colspan", "0");
					if (!uhid) return true;
					var th = null;
					ths.each(function(idx, thx) {
						if ($(thx).attr(self.identity) == uhid) {
							th = thx;
							return false;
						}
					});
					th && $(o).data("uper", th);
				});
			};
			// Indexs
			var index = 0;
			this.$table.find("thead>tr>th").each(function(i, o) {
				if (isMultiple && !$(o).attr(self.attr)) {
					return;
				}
				//var idx = index++;
				var idx = $(o).attr("index") || $(o).attr("hide-index");
				(idx == undefined) && (idx = index++);
				$(o).data("hide-index", ~~idx);
				self.list.push({
					index: ~~idx,
					text: $(o).html(),
					th: o
				});
			});
			// Methods
			this.filter = options["filter"] || function(i, o) {
				return false
			};
			this.toggle = function() {
				this.$container.toggle(arguments);
			};
			tablehide.method["draw"].call(this);
			this.onDestroy = tablehide.method["destroy"];
			// Insert to DOM
			this.$container.appendTo(document.body).hide();;
			return this;
		},
		draw: function() {
			var self = this,
				list = self.list,
				wrap = self.$list,
				sts = tablehide.styles;
			for (var i in list) {
				var item = $(htmlz(sts["$item"])).appendTo(wrap).find("div.ui.checkbox");
				var li = list[i]
				$(htmlz(sts["$checkbox"])).attr({
					index: li["index"]
				}).appendTo(item).data("hide-th", li["th"]);
				$(htmlz("label")).append(li["text"]).appendTo(item);
			}
			// checkbox callback methods
			var multiFunc = function(hth, flg, col) {
				col = col || 1;
				var $th = $(hth);
				var _col = $th.attr("colspan") || 1;
				if (!flg) {
					var b = ~~_col - col;
					$th.attr("colspan", b);
					!(b) && $th.hide();
				} else {
					col++;
					$th.attr("colspan", ~~_col + 1);
					$th.css("display") == "none" && $th.show();
				}
				var uth = $(hth).data("uper");
				uth && arguments.callee(uth, flg, col);
			};

			this.$container.find(".ui.checkbox").checkbox({
				onDisable: function() {
					var index = $(this).attr("index");
					var table = self.$table;
					if (!self.isMultiple) {
						table.find("thead>tr>th:eq(" + index + ")").hide();
					} else {
						var hth = $(this).data("hide-th");
						var col = $(hth).attr("colspan") || 1;
						multiFunc(hth, false, ~~col);
					}
					table.find("tbody>tr>td:nth-child(" + (~~index + 1) + ")").hide();
				},
				onEnable: function() {
					var index = $(this).attr("index");
					var table = self.$table;
					if (!self.isMultiple) {
						table.find("thead>tr>th:eq(" + index + ")").show();
					} else {
						var hth = $(this).data("hide-th");
						multiFunc(hth, true, -1);
					}
					table.find("tbody>tr>td:nth-child(" + (~~index + 1) + ")").show();
				}
			});
			// status initialize 
			this.$container.find(".ui.checkbox").each(function(i, o) {
				// customer function
				var hide = self.filter(i, $(o).find("input").data("hide-th"));
				var state = hide ? "disable" : "enable";
				$(this).checkbox(state);
			});
		},
		destroy: function() {
			this.$container && this.$container.remove();
			this.$table.find("thead>tr>th").each(function(i, o) {
				$(o).removeData("uper");
			});
			for (var i in this) {
				delete this[i];
			}
		}
	};

	// Datagrid Plugin
	var datagrid = function(table) {
		var arg = arguments[1],
			options = {};
		if (typeof arg == "object") {
			options = arg;
		}
		return datagrid.method["initialize"].call({}, table, options);
	};

	hy.datagrid = datagrid;

	datagrid.defaults = {
		thead_columns: null,
		field_columns: null,
		data: null,
		form: null,
		params: null,
		url: null,
		method: "post",
		pagination: null,
		sort: null,
		loader: null,
		operation: null,
		onComplete: function(xhr, ts, state) {},
		beforeLoad: function(params) {},
		afterLoad: function(table) {}
	};

	datagrid.styles = {
		"$loader": "div.ui.disabled.inverted.dimmer>div.ui.text.loader"
	};
	datagrid.method = {
		initialize: function(table, options) {
			if (!table) return;
			options = $.extend({}, datagrid.defaults, options);
			// HTML Elements
			this.$table = $(table);
			this.$thead = this.$table.find("thead");
			!(this.$thead.length) && (this.$thead = $("<thead></thead>").appendTo(this.$table));
			this.$tbody = this.$table.find("tbody");
			!(this.$tbody.length) && (this.$tbody = $("<tbody></tbody>").appendTo(this.$table));
			this.$pagination = null;
			// loader
			var loader = options["loader"];
			var sts = datagrid.styles;
			loader && (this.$loader = $(htmlz(sts["$loader"])));
			if (typeof loader == "string") {
				this.$loader.find(".ui.text.loader").append(loader);
				this.$table.before(this.$loader);
			} else if (loader && (typeof loader == "object")) {
				loader["coverage"] && $(loader["coverage"]).append(this.$loader);
				loader["text"] && this.$loader.find(".ui.text.loader").append(loader["text"]);
			}
			// Variables
			this.thead_columns = options["thead_columns"];
			this.field_columns = options["field_columns"];
			this.data = options["data"];
			this.url = options["url"];
			this.method = options["method"];
			this.params = options["params"];
			this.form = options["form"];
			this.total = null;
			this.mapping = {};
			this.selected = {};
			this.pagination = options["pagination"];
			this.operation = options["operation"];

			// Handler
			datagrid.method["handleThead"].call(this);
			// mapping 建立字段与表头的映射关系
			var self = this;
			var hasNoField = !this.field_columns;
			var isMultiple = this.$thead.find("tr").length > 1;
			var index = 0;
			(hasNoField) && (this.field_columns = []);
			this.$thead.find("tr>th").each(function(idx, th) {
				var $th = $(th),
					field = $th.attr("field");
				if (field) {
					var idx = $th.attr("index") || $th.attr("field-index");
					(idx == undefined) && (idx = index++);
					$(th).data("field-index", ~~idx);
					self.mapping[field] = $th;
					!isMultiple && self.field_columns.push(field);
				}
			});
			// Methods
			this.onComplete = options["onComplete"];
			this.beforeLoad = options["beforeLoad"];
			this.afterLoad = options["afterLoad"];
			this.onDestroy = datagrid.method["destroy"];

			datagrid.method["handleTbody"].call(this, "init");
			// sort
			var sort = options["sort"];
			if (sort) {
				var sort_opt = {};
				(typeof sort == "object") && (sort_opt = sort);
				sort_opt["attr"] = "field";
				this.tablesort = hy.tablesort(table, sort_opt);
			}
			return this;
		},
		handleThead: function() {
			var thds = this.thead_columns;
			if (!thds) return;
			var self = this,
				thead = self.$thead,
				fields = self.field_columns;
			thead.html("");
			var isMultiple = (thds.length && hy.isArray(thds[1]));
			// methods
			var multiFunc = function(thds) {
				for (var i in thds) {
					var tr = $("<tr></tr>"),
						ths = thds[i];
					for (var k in ths) {
						var th = $("<th></th>").appendTo(tr),
							cell = ths[k];
						var text = cell["text"] || "";
						(typeof cell == "string") && (text = cell);
						th.append(text);
						if (typeof cell == "object") {
							for (var s in cell) {
								th.attr(s, cell[s]);
							}
						}
					}
					tr.appendTo(self.$thead);
				}
			};

			var singleFunc = function(thds) {
				var tr = $("<tr></tr>");
				for (var i in thds) {
					var th = $("<th></th>").appendTo(tr),
						cell = thds[i];
					var text = cell["text"] || "";
					(typeof cell == "string") && (text = cell);
					th.append(text);

					if (typeof cell == "object") {
						for (var s in cell) {
							th.attr(s, cell[s]);
						}
					}
				}
				tr.appendTo(self.$thead);
			};

			isMultiple ? multiFunc(thds) : singleFunc(thds);

		},
		handleTbody: function(state) {
			var self = this,
				tbody = this.$tbody;
			var complete = function(xhr, ts) {
				// sort tabel after pagination plugin on change 
				if (state == "reset" && this.tablesort) {
					var sort = this.tablesort;
					this.$thead.find("tr>th").each(function(i, th) {
						var $th = $(th);
						var asc = $th.hasClass(sort["asc"]),
							desc = $th.hasClass(sort["desc"]);
						var isSort = asc || desc;
						if (isSort) {
							var index = $th.data("sort-index");
							sort["direction"][~~index] = asc ? "desc" : "asc";
							$th.trigger("click.sort");
						}
					});
				}
				self.onComplete.call(self, xhr, ts, state);
			}
			var error = function(xhr, status, error) {
				var tr = $("<tr></tr>").appendTo(tbody);
				tr.append("<td colspan=\"" + self.field_columns.length + "\">加载数据发生错误! 返回状态: " + status + "</td>");
			};
			// load data
			if (state == "init" && self.data) {
				datagrid.method["draw"].call(self, self.data);
				return;
			}
			datagrid.method["load"].call(self, function() {
				if (this.data.length) {
					datagrid.method["draw"].call(this, this.data);
					// pagination plugin
					var page = this.pagination;
					var container = null;
					if (page) {
						container = page["container"] || page;
					}
					if (state == "init" && container) {
						var options = {};
						(typeof page == "object") && (options = page);
						options["totalRows"] = this.total;
						options["onChange"] = function(pager) {
							self.params = pager;
							datagrid.method["handleTbody"].call(self, "reset");
						};
						this.$pagination = hy.pagination(container, options);
					}
				} else {
					var tr = $("<tr></tr>").appendTo(tbody);
					tr.append("<td colspan=\"" + self.field_columns.length + "\">未找到数据</td>");
				}
			}, complete, error);
		},
		draw: function(data) {
			if (!data.length) return;
			var self = this,
				fields = self.field_columns,
				mapping = self.mapping,
				tbody = self.$tbody;
			tbody.html("");
			// type is "checkbox" or "radio" ,choice..
			var checkFunc = function(tr, field, type) {
				var clazz = "div.ui." + ((type == "radio") ? "radio." : ".") + "checkbox";
				var func = field["onChange"] || function(data) {};
				tr.append(htmlz("td > " + clazz + ">input[type='" + type + "',name='" + (field["field"] || field) + "'] + label"));

				// checked initialize...
				if (self.selected) {
					var id = tr.attr("identity");
					(id in self.selected) && tr.find(".ui.checkbox").checkbox("enable");
				}

				tr.find(".ui.checkbox").checkbox({
					onChange: function() {
						var ctr = $(this).parents("tr");
						var data = ctr.data("data"),
							id = ctr.attr("identity");
						var isSelected = $(this)[0].checked;
						(!self.selected) && (self.selected = {});
						if (isSelected) {
							if (type == "radio") self.selected = {};
							!(id in self.selected) && (self.selected[id] = data);
						} else {
							(id in self.selected) && delete self.selected[id];
						}
						// callback method
						func.call(self, self.selected);
					}
				});
			};
			for (var i in data) {
				var tr = $("<tr></tr>").appendTo(tbody);
				// data cache
				tr.data("data", data[i]);
				for (var f in fields) {
					var field = fields[f];
					var _field = field["field"] || field,
						map = mapping[_field];
					if (typeof _field != "string") throw Error("[ERROR]: 插件无法获取字段名称,请指定字段名称! {field:\"name\"}");
					// identity field
					var identity = field.identity || (map && map.attr("identity"));
					if (identity) {
						tr.attr("identity", data[i][_field]);
					}

					var ck = field.checkbox || (map && map.attr("checkbox"));
					var rd = field.radio || (map && map.attr("radio"));
					// add content
					(ck) && checkFunc(tr, field, "checkbox");
					(rd) && checkFunc(tr, field, "radio");
					(!ck && !rd) && (tr.append("<td>" + data[i][_field] + "</td>"))
					// if td have attributes ... do something
				}
				if (self.operation) {
					tr.append("<td>" + self.operation + "</td>");
				}
			}
			// hide cells
			self.$thead.find("tr>th").each(function(idx, th) {
				var $th = $(th),
					field = $th.attr("field");
				if (field && $th.css("display") == "none") {
					var index = $th.data("field-index");
					tbody.find("tr>td:nth-child(" + (~~index + 1) + ")").hide();
				}
			});
			self.afterLoad.call(self, self.$table);
		},
		load: function(success, complete, error) {
			var self = this,
				loader = self.$loader;
			complete = complete || function() {};
			error = error || function() {};
			// 组装数据
			self.beforeLoad(self.params);
			// merge
			var merge = function(params) {
				var buff = [];
				if (typeof params == "object") {
					for (var i in params) {
						var type = typeof params[i];
						if (type == "string" || type == "number" || type == "boolean") {
							buff.push(i + "=" + params[i]);
						}
					}
					return buff.join("&");
				}
				return params;
			};
			var params = null;

			if (self.form) {
				params = self.form.serialize();
			}
			(params) && (params += merge(self.params));
			!(params) && (params = merge(self.params));
			params = params || {};
			// loader 
			loader && loader.removeClass("disabled").addClass("active");
			// data source
			$.ajax({
				url: self.url,
				type: self.method,
				dataType: "json",
				data: params,
				success: function(data) {
					self.total = data["total"] || 0;
					self.data = data["data"] || [];
					success.call(self);
				},
				complete: function(xhr, ts) {
					loader && loader.removeClass("active").addClass("disabled");
					complete.call(self, xhr, ts);
				},
				error: function(xhr, status, err) {
					error.call(self, xhr, status, err);
				}
			});
		},
		destroy: function() {
			this.$loader && this.$loader.remove();
			this.$pagination && this.$pagination.onDestroy();
			this.tablesort && this.tablesort.onDestroy();
			for (var i in this) {
				delete this[i];
			}
		}
	};
})(jQuery, window, document, undefined);